package com.wyw.function.learning.demo6;

import com.wyw.function.learning.common.CaloricLevel;
import com.wyw.function.learning.common.Dish;
import com.wyw.function.learning.common.DishClient;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @Title   用流收集数据
 * @Description 分组
 * @Author Mr Wu yewen.wu.china@gmail.com
 * Update History:
 * Author        Time            Content
 */
public class DemoClient14 {
    /**
     * 把肉放一组，鱼放一组，其他放一组
     */
//    public static void main(String[] args) {
//        List<Dish> menu = DishClient.getMenu();
//        Map<Dish.Type, List<Dish>> collect = menu.stream().collect(Collectors.groupingBy(Dish::getType));
//        System.out.println(collect);
//    }

    /**
     * 把热量不到400卡路里的菜分为“低热量”（diet），热量400-700分为“普通”（normal），高于700分为“高热量”（fat）
     */
//    public static void main(String[] args) {
//        List<Dish> menu = DishClient.getMenu();
//        Map<CaloricLevel, List<Dish>> collect = menu.stream().collect(Collectors.groupingBy(dish -> {
//            if (dish.getCalories() <= 400)
//                return CaloricLevel.DIEF;
//            else if (dish.getCalories() <= 700)
//                return CaloricLevel.NORMAL;
//            else
//                return CaloricLevel.FAT;
//        }));
//        System.out.println(collect);
//    }

    /**
     * 多级分组
     */
//    public static void main(String[] args) {
//        List<Dish> menu = DishClient.getMenu();
//        Map<Dish.Type, Map<CaloricLevel, List<Dish>>> collect = menu.stream().collect(Collectors.groupingBy(Dish::getType, Collectors.groupingBy(
//                dish -> {
//                    if (dish.getCalories() <= 400)
//                        return CaloricLevel.DIEF;
//                    else if (dish.getCalories() <= 700)
//                        return CaloricLevel.NORMAL;
//                    else
//                        return CaloricLevel.FAT;
//                })
//        ));
//        System.out.println(collect);
//    }

    /**
     * 按子组收集数据,菜单中每类菜的种类
     */
//    public static void main(String[] args) {
//        List<Dish> menu = DishClient.getMenu();
//        Map<Dish.Type, Long> collect = menu.stream().collect(Collectors.groupingBy(Dish::getType, Collectors.counting()));
//        System.out.println(collect);
//
//        // 最高热量的菜肴按照类型进行分类
//        // 这个Map中的值是Optional，因为这是maxBy工厂方法生成的收集器类型，但实际上，如果菜单没有某一个类型的Dish，这个类型就不会对应一个Optional.empty()值，而且根本不会出现在Map中
//        // groupingBy收集器只有应用分组条件后，第一次在流中找到某个键对应的元素才会把键加入到分组Map中。
//        // 这意味着Optional包装器在这里不是很有用，因为它不会仅仅因为他是归约收集器的返回类型而表打一个最终可能不存在却意外存在的值
//        Map<Dish.Type, Optional<Dish>> collect1 = menu.stream()
//                .collect(
//                        Collectors.groupingBy(Dish::getType, Collectors.maxBy(Comparator.comparing(Dish::getCalories)))
//                );
//        System.out.println(collect1);
//    }

    /**
     * 把收集器的结果转换为另一种类型
     */
    public static void main(String[] args) {
        List<Dish> menu = DishClient.getMenu();
        Map<Dish.Type, Dish> collect = menu.stream().collect(
                Collectors.groupingBy(
                        Dish::getType, Collectors.collectingAndThen(Collectors.maxBy(Comparator.comparing(Dish::getCalories)), Optional::get)
                )
        );
        System.out.println(collect);

        // 求所有热量总和
        Map<Dish.Type, Integer> collect1 = menu.stream().collect(
                Collectors.groupingBy(Dish::getType, Collectors.summingInt(Dish::getCalories))
        );
        System.out.println(collect1);

        // 常常和groupingBy联合使用的另一个收集器是mapping方法生成的。
        // 这个方法接收两个参数：一个函数对流中的元素做变化，另一个则将变化的结果收集起来。
        // 其目的就是在累加前对每个输入元素应用一个映射函数，这样就可以让接收特定类型元素的收集器适用不同类型的对象。
        // 对于，每种类型的Dish，菜单中都有哪些CaloricLevel。
        Map<Dish.Type, Set<CaloricLevel>> collect2 =
                menu.stream().collect(
                        Collectors.groupingBy(Dish::getType, Collectors.mapping(
                            dish -> {
                                if (dish.getCalories() <= 400)
                                    return CaloricLevel.DIEF;
                                else if (dish.getCalories() <= 700)
                                    return CaloricLevel.NORMAL;
                                else
                                    return CaloricLevel.FAT;
                            }, Collectors.toSet())
                        )
                );
        System.out.println(collect2);

        // 可以指定具体的实现类
        Map<Dish.Type, HashSet<CaloricLevel>> collect3 =
                menu.stream().collect(
                        Collectors.groupingBy(Dish::getType, Collectors.mapping(
                            dish -> {
                                if (dish.getCalories() <= 400)
                                    return CaloricLevel.DIEF;
                                else if (dish.getCalories() <= 700)
                                    return CaloricLevel.NORMAL;
                                else
                                    return CaloricLevel.FAT;
                            }, Collectors.toCollection(HashSet::new))
                        )
                );
        System.out.println(collect3);
    }


}

